#include<assert.h>
#include<string.h>
#include<stdlib.h>
#include<iostream>
#include<initializer_list>
#include<algorithm>
using namespace std;


//string: 本质是管理字符串的数组,数组结尾有\0

namespace Mango
{
class string 
{
public:
  //string的迭代器是原生指针
  typedef char* iterator;
  typedef const char* const_iterator;
  //默认构造函数
  string(const char* s = "") 
  {
    str = new char[strlen(s)+1];
    strcpy(str,s);
    _size = _capacity = strlen(s);
  }
  //传统写法:构造和要拷贝对象一样大的空间,然后把内容拷贝下来
  string(const string& s)
  {
    str = new char[strlen(s.str)+1];
    strcpy(str,s.str);
    _size = _capacity = strlen(s.str);
    cout << "string(const string& s)" << endl;
  }
  void swap(string& s)
  {
    ::swap(str,s.str);
    ::swap(_size,s._size);
    ::swap(_capacity,s._capacity);
  }
  //现代写法:复用构造函数构造一个对象，然后交换给当前对象
  //string(const string& s)
  //{
  //  string tmp(s.str);
  //  swap(tmp);
  //  cout << "string(const string& s)" << endl; 
  //}
  
  //传统写法
  string& operator=(const string& s)
  {
    cout << "operator=(const string& s)" << endl;
    //防止自己给自己赋值
    //不建议先释放原来的空间,因为new可能会失败,抛异常，如果先释放,那么就可能导致str为野指针
    if(this != &s)
    {
        char* tmp = new char[strlen(s.str)+1];
        strcpy(tmp,s.str);
        delete[] str;
        str = tmp;
        _size = s._size;
        _capacity = s._capacity;
    }
    return *this;
  }
  //现代写法 
  //string& operator=(const string& s)
  //{
  // cout << "operator=(const string& s)" << endl;
  //  if(this != &s)
  //  {
  //    string tmp(s.str);
  //    swap(tmp);
  //  }
  //  return *this;
  //}
 
  //凡是构造函数,需要把指针初始化为空,否则下面交换就会出问题
  string(string&& s) :str(nullptr)
  {
    cout <<"string(string&& s)" << endl;
    swap(s);
  }
  string& operator=(string&& s)
  {
    cout << "string& operator=(string&& s)" << endl;
    swap(s);
    return *this;
  }
  ~string()
  {
    if(str)
    {
      delete[] str;
      str = nullptr;
    }
    _size = _capacity = 0;
  }
  //迭代器相关函数
  iterator begin()
  {
    return str;
  }
  iterator end()
  {
    return str + _size;
  }
  const_iterator begin()const 
  {
    return str;
  }
  const_iterator end()const 
  {
    return str+_size;
  }

  //容量和大小相关函数
  size_t size()
  {
    return _size;
  }
  size_t capacity()
  {
    return _capacity;
  }
  void reserve(size_t n)
  {
    //reserve只改变容量，不改变数据个数
    if(n > capacity())
    {
      char* tmp = new char[n];
      //不建议使用strcpy，因为strcpy拷贝的时候遇到\0就停止拷贝了，而\0可能是有效字符，所以我们需要按个数拷贝
      strncpy(tmp,str,_size+1); //把\0也拷贝过去
      delete[] str;
      str = tmp;
      _capacity = n;
    }
  }
  void resize(size_t n, char ch = '\0')
  {
    if(n < size()) //减少数据个数
    {
      str[n] = '\0';
      _size = n;
    }
    else  //需要增加数据个数
    {
      if(n > capacity())//判断是否需要扩容
      {
        reserve(n);
      }
      //插入数据，若没有指定，默认插入的是\0
      for(size_t i = _size;i<n;i++)
      {
        str[i] = ch;
      }
      str[n] = '\0';//注意要放\0！！！ 
      _size = n;
    }
  }
  bool empty()const 
  {
    return _size == 0;
  }

  //修改字符串相关函数
  void push_back(char ch)
  {
    //先检查是否需要扩容
    if(_size == _capacity)
    {
      int newCapacity = _capacity == 0 ?10:_capacity *2;
      reserve(newCapacity);
    }
    str[_size++] = ch;
    str[_size] = '\0';
  }
  void append(const char* s)
  {
    size_t len = size() + strlen(s);
    if(len > capacity())
    {
      reserve(len);
    }
    strcpy(str+_size,s); //把该字符串拷贝到原字符串\0往后位置
    _size = len;
  }
  string& operator+=(char ch)
  {
    push_back(ch);
    return *this;
  }
  string& operator+=(const char* str)
  {
    append(str);
    return *this;
  }
  string& insert(size_t pos, char ch)
  {
    assert(pos <= _size); //可以在_size位置插入
    //检查是否需要扩容
    if(_size == _capacity)
    {
      int newCapacity = _capacity == 0 ? 10:_capacity * 2;
      reserve(newCapacity);
    }
    size_t end = _size+1;
    while(end > pos) //不能让end和pos比较相等，因为size_t类型，如果pos为0,那么end >= 0恒成立，会导致死循环
    {
      str[end] = str[end-1];
      end--;
    }
    str[pos] = ch;
    _size++;
    return *this;
  }
  string& insert(size_t pos, const char* s)
  {
    assert(pos <= _size); //可以在_size位置插入
    size_t len = strlen(s);
    if(len+_size > _capacity)
    {
      reserve(len+_size);
    }
    //pos之后的字符都往后挪动len个单位
    char* end = str + _size -1; 
    while(end >= str + pos)
    {
      *(end+len) = *end;
      end--;
    }
    //这里要使用的是strncpy,防止把str中的\0也拷贝到了字符串当中
    strncpy(str+pos,s,strlen(s));
    _size += len;
    return *this;
  }
  string& erase(size_t pos, size_t len = npos)
  {
    //0.检查pos的合法性
    assert(pos < _size);
    //1.先计算pos往后还有多少个字符
    size_t sz = _size - pos;
    //case1:要删除的个数 > 剩余的字符个数,说明pos往后的字符都要删掉
    if(len > sz)
    {
      str[pos] = '\0';
      _size = pos;
    }
    else 
    {
      //[_str+pos,_str + pos + len-1] 是要删除的字符,把后面的字符覆盖到_str+pos位置即可
      strcpy(str+pos,str+pos+len);
      _size -= len;
    }
    return *this;
  }
  void clear()
  {
    //容量并不会清0，仅仅是把数据个数清0
    _size = 0;
    str[_size] = '\0';
  }
  const char* c_str()const 
  {
    return str;
  }

  //访问字符串相关函数
  char& operator[](size_t i)
  {
    assert(i < _size);
    return str[i];
  }
  const char& operator[](size_t i)const 
  {
    assert(i < _size);
    return str[i];
  }
  size_t find(char ch, size_t pos = 0)const 
  {
      assert(pos < _size); //要保证pos的合法性
      for(size_t i = pos;i<_size;i++)
      {
        if(str[i] == ch)
        {
          return i;
        }
      }
      return npos;
  }
  size_t find(const char* s, size_t pos = 0)const 
  {
    assert(pos < _size); //要保证pos的合法性
    char* ret = strstr(str+pos,s); //在str+pos位置往后找子串s
    if(ret!=nullptr)
    {
      return ret - str;
    }
    else 
    {
      return npos;
    }
  }
  //关系:pos+pos' = _size-1
  //在pos位置开始反向查找ch的位置==>相当于在_size-1-pos位置开始正向查找字符串逆置之后的ch的位置记为ret 
  //则该字符所在位置就是ret + _size -1
  size_t rfind(char ch, size_t pos = npos)const 
  {
    char* tmp = new char[_capacity+1];
    strcpy(tmp,str);
    reverse(tmp,tmp+_size); //左闭右开
    if(pos >= _size) //如果pos为npos也会进入此判断当中
    {
      pos = _size-1;
    }
    pos = _size-1-pos; //拿到pos'位置
    size_t ret = find(ch,pos);
    if(ret != npos)
    {
      return  _size -1 - ret;
    }
    else 
    {
      return npos;
    }
  }
  //关系运算符重载函数 由于第一个参数是默认的this指针，所以只需要显示的写一个参数即可
  //也可以在类外实现，具体是否在类中声明为内联要看该函数有没有访问类的私有/保护成员
  bool operator>(const string& s)const 
  {
    return strcmp(str,s.str) > 0;
  }
  bool operator>=(const string& s)const 
  {
    return *this>s || *this == s; //*this>s ==> this->operator>(s)
  }
  bool operator<(const string& s)const 
  {
    return strcmp(str,s.str) < 0;
  }
  bool operator<=(const string& s)const 
  {
    return *this < s || *this == s;
  }
  bool operator==(const string& s)const 
  {
    return strcmp(str,s.str) == 0;
  }
  bool operator!=(const string& s)const 
  {
    return !(*this == s);
  }
private:
  char* str;
  size_t _capacity = 0;
  size_t _size = 0;
  static size_t npos;
};
size_t string::npos = -1;

//下述的三个函数返回引用是为了支持连续的输入/输出
//流提取和流插入,不建议写在类,因为类的非静态成员函数第一个参数是默认的this指针,流对象和string对象抢占第一个操作数的位置
istream& operator>>(istream& in,string& s)
{
  //注意:需要先清空字符串原有内容再输入,遇到空格||换行就停止输入
  s.clear();
  char ch = in.get();//get方法：读取一个字符
  while(ch != ' ' && ch != '\n')
  {
    s += ch;
    ch = in.get();
  }
  return in;
}
ostream& operator<<(ostream& out,string& s)
{
  //注意:不能直接out<<s.c_str() 因为字符串关注的是\0,而string关注的是字符串大小,有可能\0也是string 的有效字符
  for(auto& c:s)
  {
    out << c;
  }
  return out;
}
istream& getline(istream& in,string& s)
{
  //方法和operator>>类似,只不过getline遇到\n才停止,可以读取带空格的字符
  s.clear();
  char ch = in.get();
  while(ch != '\n')
  {
    s += ch;
    ch = in.get();
  }
  return in;
}

//测试反向查找
void testString8()
{
  string tmp("Mango Hello world");
  cout << tmp.rfind('e') << endl;
}
//测试现代写法的成员函数
void testString1()
{
  string s0;
  string s1("Always");
  string s2(s1);
  cout << s2.c_str() << endl;
  string s3("more than words");
  s3 = s1;
  cout << s3.c_str() << endl;
  s3 = s3;
}
//测试流插入流提取运算符重载
void testString7()
{
  //string s;
  string s("Hello Mango");
  cin >> s;
  cout << s << endl;
  // 不能以字符串形式输出,测试标准库
  string s1("more than");
  s1 += '\0';
  s1 += "words";
  cout << s1 << endl;
  cout << s1.c_str() << endl;
}
// 测试比较大小运算符重载
void testString6()
{
  string s1("abcd");
  string s2("abcd");
  cout << (s1 <= s2) << endl;
  string s3("abcd");
  string s4("abcde");
  cout << (s3 <= s4) << endl;
  string s5("abcde");
  string s6("abcd");
  cout << (s5 <= s6) << endl;
}
// 测试insert和erase
void testString5()
{
  string s(" Mango Hello");
  s.insert(0, "Lemon");
  cout << s.c_str() << endl;
  s.insert(5, '!');
  cout << s.c_str() << endl;
  s.erase(0, 7);
  cout << s.c_str() << endl;
  s.erase(6);
  cout << s.c_str() << endl;
}
// 测试查找
void testString4()
{
  string s("Mango");
  cout << s.find('a') << endl;
  cout << s.find("ng") << endl;
  cout << s.find("max") << endl;
}
// 测试resize
void testString3()
{
  string s("Mango"); // capacity - 12
  s.resize(5);
  cout << s.c_str() << endl;
  s.resize(7, '!');
  cout << s.c_str() << endl;
  s.resize(20, '~');
  cout << s.c_str() << endl;
}
// 测试尾插字符及字符串push_back/append,同时测试reserve
void testString2()
{
  string s("more than words");
  s.push_back('~');
  s.push_back(' ');
  cout << s.c_str() << endl;
  s.append("zhabuduodele");
  cout << s.c_str() << endl;
  s += '~';
  s += "yiyandingzhen";
  cout << s.c_str() << endl;
}
}
